/**
 *   Copyright (C) 2011 - Casey Burkhardt
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
**/

package com.tightometer.android;

import com.admob.android.ads.AdListener;
import com.admob.android.ads.AdView;
import com.tightometer.android.R;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ScrollView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.RelativeLayout.LayoutParams;
import android.widget.SeekBar.OnSeekBarChangeListener;

import java.util.ArrayList;
import java.util.List;

public class TightometerActivity extends Activity {
    private TightometerActivity self;

    public static int TIGHTNESS_LEVELS = 10;

    private String mVersion;
    
    private SharedPreferences mSettings;
    
    private PackageInfo mPackages;
    
    private ScrollView mScroll;

    private TextView mTightnessDescriptionView, mInstanceInfoTitle, mInstanceInfoMessage;

    private TightnessRefresherTask mRefresher;
    
    private TightnessUpdaterTask mUpdater;

    private SeekBar mTightometer;

    private Button mRefreshButton, mShareButton, mInstanceInfoButton;
    
    private AdView mAd;
    
    private ArrayList<MediaPlayer> mTightnessSounds;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        // Initialization
        super.onCreate(savedInstanceState);
        self = this;
        setContentView(R.layout.main);
        
        // Shared Preferences Instance
        // Restore demo preferences if none are set.
        mSettings = PreferenceManager.getDefaultSharedPreferences(this);
        if (mSettings.getString(getString(R.string.pref_instance_url), getString(R.string.utility_empty)).equals(getString(R.string.utility_empty))) {
            SharedPreferences.Editor editor = mSettings.edit();
            editor.putString(getString(R.string.pref_instance_url), getString(R.string.pref_default_instance_url));
            editor.putString(getString(R.string.pref_instance_password), getString(R.string.pref_default_instance_password));
            editor.commit();
        }
        
        // Package Information Instance
        try {
            mPackages = getPackageManager().getPackageInfo(this.getPackageName(), PackageManager.GET_META_DATA);
        } catch(NameNotFoundException e) {
            mVersion = getString(R.string.parameter_default_client_version);
        }
        mVersion = getString(R.string.utility_empty) + mPackages.versionCode;
            
        // Tightness Description View
        mTightnessDescriptionView = (TextView) findViewById(R.id.status);
        
        // Tightness Adjustment Seekbar
        mTightometer = (SeekBar) findViewById(R.id.tightometer);
        mTightometer.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                updateTightnessLevel((int)(seekBar.getProgress() / ((float) seekBar.getMax()) * 100));
            }
            
            // Unimplemented Stubs
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {}
            public void onStartTrackingTouch(SeekBar seekBar) {}
        });
        
        // Refresh Tightometer Button
        mRefreshButton = (Button) findViewById(R.id.refresh);
        mRefreshButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                refreshTightnessLevel();
            }
        });
        
        // Share Instance Website Button
        mShareButton = (Button) findViewById(R.id.share);
        mShareButton.setOnClickListener(new OnClickListener() {          
            @Override
            public void onClick(View v) {
                Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND);
                shareIntent.setType(getString(R.string.utility_share_content_type));
                shareIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, getString(R.string.share_subject));
                shareIntent.putExtra(android.content.Intent.EXTRA_TEXT, mSettings.getString(getString(R.string.pref_instance_url), getString(R.string.url_tightometer_home)));
                startActivity(Intent.createChooser(shareIntent, getString(R.string.share_method)));
            }
        });
        
        // Private Instance Info Pane (2 TextViews + Button)
        mInstanceInfoTitle = (TextView) findViewById(R.id.info_title);
        mInstanceInfoMessage = (TextView) findViewById(R.id.info_message);
        
        mInstanceInfoButton = (Button) findViewById(R.id.instance_info);
        mInstanceInfoButton.setOnClickListener(new OnClickListener() {          
            @Override
            public void onClick(View v) {
                Intent launchBrowser = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_tightometer_get))); 
                startActivity(launchBrowser);
            }
        });

        // Media Player Objects for Sound Effects
        mTightnessSounds = new ArrayList<MediaPlayer>(TIGHTNESS_LEVELS);
        mTightnessSounds.add(MediaPlayer.create(this, R.raw.pos1));
        mTightnessSounds.add(MediaPlayer.create(this, R.raw.pos2));
        mTightnessSounds.add(MediaPlayer.create(this, R.raw.pos3));
        mTightnessSounds.add(MediaPlayer.create(this, R.raw.pos4));
        mTightnessSounds.add(MediaPlayer.create(this, R.raw.pos5));
        mTightnessSounds.add(MediaPlayer.create(this, R.raw.pos6));
        mTightnessSounds.add(MediaPlayer.create(this, R.raw.pos7));
        mTightnessSounds.add(MediaPlayer.create(this, R.raw.pos8));
        mTightnessSounds.add(MediaPlayer.create(this, R.raw.pos9));
        mTightnessSounds.add(MediaPlayer.create(this, R.raw.pos10));
        
        // Scroll View, which should fill the display vertically unless an ad is displayed.
        mScroll = (ScrollView) findViewById(R.id.scroll);
        
        // Set up AdMob Display Ads
        mAd = (AdView) findViewById(R.id.ad);
        mAd.requestFreshAd();
        mAd.setAdListener(new AdListener() {
            @Override
            public void onReceiveRefreshedAd(AdView arg0) {
                if (arg0.hasAd()) {
                    onReceiveAd(arg0);
                }
            }
            
            @Override
            public void onReceiveAd(AdView arg0) {
                LayoutParams params = (LayoutParams) mScroll.getLayoutParams();
                params.setMargins(0, 0, 0, 70);
                mScroll.setLayoutParams(params);
            }
            
            @Override
            public void onFailedToReceiveRefreshedAd(AdView arg0) {
                if (!arg0.hasAd()) {
                    onFailedToReceiveAd(arg0);
                }
            }
            
            @Override
            public void onFailedToReceiveAd(AdView arg0) {
                LayoutParams params = (LayoutParams) mScroll.getLayoutParams();
                params.setMargins(0, 0, 0, 0);
                mScroll.setLayoutParams(params);                
            }
        });
    }
    
    @Override
    public void onResume() {
        super.onResume();
        
        // Show private instance info pane if necessary
        if(mSettings.getString(getString(R.string.pref_instance_url), getString(R.string.pref_default_instance_url)).equals(getString(R.string.pref_default_instance_url))) {
            mInstanceInfoTitle.setVisibility(View.VISIBLE);
            mInstanceInfoMessage.setVisibility(View.VISIBLE);
            mInstanceInfoButton.setVisibility(View.VISIBLE);
        } else {
            mInstanceInfoTitle.setVisibility(View.GONE);
            mInstanceInfoMessage.setVisibility(View.GONE);
            mInstanceInfoButton.setVisibility(View.GONE);
        }
        refreshTightnessLevel();
    }

    @Override
    public void onPause() {
        // Stray network requests that complete on the background thread after the view is unloaded will cause a force close.
        // If a network request is active during application shutdown, force cancel the AsyncTask request.
        super.onPause();
        if (mRefresher != null) {
            mRefresher.cancel(true);
        }
        if (mUpdater != null) {
            mUpdater.cancel(true);
        }
    }
    
    private void refreshTightnessLevel() {
        mTightnessDescriptionView.setText(R.string.status_refreshing);
        mRefresher = new TightnessRefresherTask();
        mRefresher.execute(mSettings.getString(getString(R.string.pref_instance_url), "") + getString(R.string.url_suffix_refresh));
    }
    
    private void updateTightnessLevel(int tightnessLevel) {
        mTightnessDescriptionView.setText(R.string.status_updating);
        mUpdater = new TightnessUpdaterTask();
        TightnessUpdate newLevel = new TightnessUpdate(mSettings.getString(getString(R.string.pref_instance_url), "") + getString(R.string.url_suffix_update), mVersion, mSettings.getString(getString(R.string.pref_instance_password), ""), tightnessLevel);

        // Play associated sound if necessary
        if (mSettings.getBoolean(getString(R.string.pref_sounds_enabled), false)) {
            if (tightnessLevel < 10) {
                mTightnessSounds.get(5).start();
            } else if (tightnessLevel < 20) {
                mTightnessSounds.get(3).start();
            } else if (tightnessLevel < 30) {
                mTightnessSounds.get(9).start();
            } else if (tightnessLevel < 40) {
                mTightnessSounds.get(2).start();
            } else if (tightnessLevel < 50) {
                mTightnessSounds.get(6).start();
            } else if (tightnessLevel < 60) {
                mTightnessSounds.get(0).start();
            } else if (tightnessLevel < 70) {
                mTightnessSounds.get(4).start();
            } else if (tightnessLevel < 80) {
                mTightnessSounds.get(1).start();
            } else if (tightnessLevel < 90) {
                mTightnessSounds.get(8).start();
            } else {
                mTightnessSounds.get(7).start();
            }
        }       
        mUpdater.execute(newLevel);
    }
    
    public void displayNetworkError() {
        mTightnessDescriptionView.setText(R.string.error_title_network);
        new AlertDialog.Builder(this)
        .setTitle(R.string.error_title_network)
        .setMessage(R.string.error_text_network)
        .setIcon(android.R.drawable.ic_dialog_alert)
        .setNeutralButton(R.string.error_button_network, null)
        .create().show();
    }
    
    public void displayConfigurationError() {
        mTightnessDescriptionView.setText(R.string.error_title_incorrect_configuration);
        new AlertDialog.Builder(this)
        .setTitle(R.string.error_title_incorrect_configuration)
        .setMessage(R.string.error_text_incorrect_configuration)
        .setIcon(android.R.drawable.ic_dialog_alert)
        .setPositiveButton(R.string.error_button_incorrect_configuration, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Intent launchSettings = new Intent(self, TightometerPreferencesActivity.class);
                startActivity(launchSettings);
                dialog.dismiss();
            }
        })
        .create().show();        
    }
    
    public void displayPasswordError() {
        mTightnessDescriptionView.setText(R.string.error_title_incorrect_password);
        new AlertDialog.Builder(this)
        .setTitle(R.string.error_title_incorrect_password)
        .setMessage(R.string.error_text_incorrect_password)
        .setIcon(android.R.drawable.ic_dialog_alert)
        .setPositiveButton(R.string.error_button_incorrect_password, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Intent launchSettings = new Intent(self, TightometerPreferencesActivity.class);
                startActivity(launchSettings);
                dialog.dismiss();
            }
        })
        .create().show();
    }
    
    public void displayInactiveInstanceError() {
        mTightnessDescriptionView.setText(R.string.error_title_inactive_instance);
        new AlertDialog.Builder(this)
        .setTitle(R.string.error_title_inactive_instance)
        .setMessage(R.string.error_text_inactive_instance)
        .setIcon(android.R.drawable.ic_dialog_alert)
        .setPositiveButton(R.string.error_button1_inactive_instance, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Intent launchBrowser = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_tightometer_home))); 
                startActivity(launchBrowser);
                dialog.dismiss();
            }
        })
        .setNegativeButton(R.string.error_button2_inactive_instance, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        })
        .create().show();
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {        
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.main_menu, menu);
        return true;
    }
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.settings:
            Intent launchSettings = new Intent(this, TightometerPreferencesActivity.class);
            startActivity(launchSettings);
            return true;
        case R.id.about:
            Intent launchBrowser = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_tightometer_about))); 
            startActivity(launchBrowser);
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }
   
    private class TightnessRefresherTask extends AsyncTask<String, Void, String> {
        @Override
        protected String doInBackground(String... urls) {
            try {
                HttpClient client = new DefaultHttpClient();
                String getURL = urls[0];
                HttpGet get = new HttpGet(getURL);
                HttpResponse responseGet = client.execute(get);
                HttpEntity resEntityGet = responseGet.getEntity();
                if (resEntityGet != null) {
                    return EntityUtils.toString(resEntityGet);
                }
            } catch (Exception e) {/* Error handling occurs on the UI thread */}
            return null;
        }

        protected void onPostExecute(String result) {
            int tightnessLevel = 0;
            if (result != null) {
                JSONObject response;
                try {
                    response = new JSONObject(result);
                    if (response.getBoolean(getString(R.string.json_key_active))) {
                        self.setTitle(self.getString(R.string.main_view_label_prefix) + " " + response.getString(getString(R.string.json_key_location)));
                        tightnessLevel = response.getInt(getString(R.string.json_key_tightness_level));
                        mTightnessDescriptionView.setText(response.getString(getString(R.string.json_key_tightness_description)));
                    } else {
                        displayInactiveInstanceError();
                    }
                } catch (Exception e) {
                    displayConfigurationError();
                }

                float ratio = tightnessLevel / 100f;
                mTightometer.setProgress((int) (ratio * mTightometer.getMax()));
            } else {
                displayNetworkError();
            }
        }
    }
 
    private class TightnessUpdate {
        protected String url;
        protected String versionCode;
        protected String password;
        protected int tightnessLevel;
        
        public TightnessUpdate(String url, String versionCode, String password, int tightnessLevel) {
            this.url = url;
            this.versionCode = versionCode;
            this.password = password;
            this.tightnessLevel = tightnessLevel;
        }
    }
    
    private class TightnessUpdaterTask extends AsyncTask<TightnessUpdate, Void, String> {
        @Override
        protected String doInBackground(TightnessUpdate... params) {
            HttpClient httpclient = new DefaultHttpClient();
            HttpPost httppost = new HttpPost(params[0].url);

            try {
                List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
                nameValuePairs.add(new BasicNameValuePair(self.getString(R.string.parameter_password), params[0].password));
                nameValuePairs.add(new BasicNameValuePair(self.getString(R.string.parameter_tightness_level), "" + params[0].tightnessLevel));
                nameValuePairs.add(new BasicNameValuePair(self.getString(R.string.parameter_client_version), params[0].versionCode));
                httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

                HttpResponse response = httpclient.execute(httppost);
                HttpEntity resEntityGet = response.getEntity();
                if (resEntityGet != null) {
                    return EntityUtils.toString(resEntityGet);
                }                
            } catch (Exception e) {/* Error handling occurs on UI thread */}
            return null;
        }
        
        protected void onPostExecute(String result) {
            if (result != null) {
                JSONObject response;
                try {
                    response = new JSONObject(result);
                    // Check for invalid password, or other failures.
                    // Refresh the Tightometer if the update was successful.
                    if (!response.getBoolean(self.getString(R.string.json_key_password_correct))) {
                        displayPasswordError();
                    } else if (response.getBoolean(self.getString(R.string.json_key_update_success))) {
                        refreshTightnessLevel();
                    } else {
                        displayConfigurationError();
                    }
                } catch (Exception e) {
                    displayConfigurationError();
                }
            } else {
                displayNetworkError();
            }
        }
    }
}
